#ifndef __FPGA_GLOBAL_MEM_APPLY_H__
#define __FPGA_GLOBAL_MEM_APPLY_H__

#include "graph_fpga.h"

#include "fpga_application.h"


#define CU_SIZE                     (4)



template <typename T>
void burstReadLite(
    uint_raw                        edge_offset,
    uint_raw                        edge_end,
    uint16                          *input,
    hls::stream<T>                  &outputstream
)
{
#pragma HLS function_instantiate variable=input
#pragma HLS function_instantiate variable=outputstream
    unsigned int offset = edge_offset/(512/NODE_WIDTH);
    unsigned int end = ((edge_end - 1) / ((512/NODE_WIDTH))) + 1;

    {

timeLineLoop : for (unsigned int i = (offset); i < (offset + end); i ++)
        {
#pragma HLS PIPELINE II=1
            T tmp = input[i];
            write_to_stream(outputstream, tmp);
        }
    }
    return;
}



void writeBackLite(int totalSize, uint16 *addr, hls::stream<burst_raw>  &input)
{
#pragma HLS function_instantiate variable=addr

    for (int i = 0; i < totalSize / (512/NODE_WIDTH); i++)
    {
#pragma HLS PIPELINE II=1
    	burst_raw tmp;
        read_from_stream(input, tmp);
        addr[i] = tmp;
    }
}


template <typename T>
void  cuDuplicate ( int               loopNum,
                    hls::stream<T>    &input,
                    hls::stream<T>    (&output)[CU_SIZE])
{
#pragma HLS function_instantiate variable=input
    for (int i = 0; i < loopNum ; i++)
    {
#pragma HLS PIPELINE II=1
        T  unit;
        read_from_stream(input, unit);
        for (int j = 0; j < CU_SIZE; j ++)
        {
#pragma HLS UNROLL
            write_to_stream(output[j], unit);
        }
    }
}


template <typename T>
void  cuMerge ( int               loopNum,
                hls::stream<T>    (&input)[CU_SIZE],
                hls::stream<T>    &output)
{
#pragma HLS function_instantiate variable=input
    for (int i = 0; i < loopNum ; i++)
    {
#pragma HLS PIPELINE II=1
        T  unit[CU_SIZE];
#pragma HLS ARRAY_PARTITION variable=unit dim=0 complete

        for (int j = 0; j < CU_SIZE; j ++)
        {
#pragma HLS UNROLL
            read_from_stream(input[j], unit[j]);
        }
        T res;
        for (int inner = 0; inner < 512/NODE_WIDTH ; inner ++)
        {
#pragma HLS UNROLL
            uint_raw tmp1 = PROP_COMPUTE_STAGE4(
                                unit[0].range(NODE_WIDTH-1 + inner * NODE_WIDTH, 0 + inner * NODE_WIDTH),
                                unit[1].range(NODE_WIDTH-1 + inner * NODE_WIDTH, 0 + inner * NODE_WIDTH)
                            );
            uint_raw tmp2 = PROP_COMPUTE_STAGE4(
                                unit[2].range(NODE_WIDTH-1 + inner * NODE_WIDTH, 0 + inner * NODE_WIDTH),
                                unit[3].range(NODE_WIDTH-1 + inner * NODE_WIDTH, 0 + inner * NODE_WIDTH)
                            );
            //uint_raw tmp3 = unit[4].range(NODE_WIDTH-1 + inner * NODE_WIDTH, 0 + inner * NODE_WIDTH);
            //uint_raw tmp3 = PROP_COMPUTE_STAGE4(
            //                    unit[4].range(NODE_WIDTH-1 + inner * NODE_WIDTH, 0 + inner * NODE_WIDTH),
            //                    unit[5].range(NODE_WIDTH-1 + inner * NODE_WIDTH, 0 + inner * NODE_WIDTH)
            //                );
            //uint_raw tmp4 = PROP_COMPUTE_STAGE4(
            //                    unit[6].range(31 + inner * 32, 0 + inner * 32),
            //                    unit[7].range(31 + inner * 32, 0 + inner * 32)
            //                );
            uint_raw tmp1_2 = PROP_COMPUTE_STAGE4(
                                tmp1,
                                tmp2
                            );
            //uint_raw tmp3_4 = PROP_COMPUTE_STAGE4(
            //                    tmp3,
            //                    tmp4
            //                );
            uint_raw rtmp = tmp1_2;//PROP_COMPUTE_STAGE4(
                                //tmp1_2,
                                //tmp3
                            //);
            res.range(NODE_WIDTH-1 + inner * NODE_WIDTH, 0 + inner * NODE_WIDTH) = rtmp;
        }

        write_to_stream(output, res);
    }
}



#endif /*__FPGA_GLOBAL_MEM_H__ */
